//============================================================================
// Name        :  SpellChecker.cpp
// Authors     :  Paulo Faria Reis
//                Ana Zaiat
// Version     :  1.0
// Date        :  17 May 2012 - 30 May 2012
// Copyright   :  Academic work - MIT License
// Description :  Spell Checker Application.
//============================================================================

#include "SpellChecker.h"

SpellChecker::SpellChecker(string fname, string dict) {
	this->fileToCheck = fname;
	this->dictionary = dict;
	this->debug = false;
}

SpellChecker::~SpellChecker() {
}

bool SpellChecker::approximateStringMatching(vector<string> & dicWords, char* toSearch, vector<option/*string*/> & options){

	int D[30][30];
	int k = 2;
	int distance = 0;
	const char* word;
	option optionWord;

	unsigned int toSearchLen = strlen(toSearch);
	int wordLen;

	string str;

	vector<string>::iterator it = dicWords.begin();
	vector<string>::iterator ite = dicWords.end();
	if(debug) cout << "Searching = " << toSearch<< " Size = " << toSearchLen << endl;
	for(; it != ite; it++){
		word=(*it).c_str();
		wordLen = (*it).size();
		// if words length differs a lot - throw away:
		if( abs(wordLen - (int)toSearchLen) > 1 ) continue;

		// Initialize
		for(unsigned int i=0; i <= toSearchLen; i++) {
			D[i][0] = i;
		}
		for(int j=0; j <= wordLen; j++) {
			D[0][j] = j;
			if(debug) cout << D[0][j] << " | ";
		}

		// Calculate distance
		if(debug) cout << "\n-----------------------------"<< endl;
		for(unsigned int i=1; i <= toSearchLen; i++) {
			for(int j=1; j <= wordLen; j++) {
				if(toSearch[i-1] == word[j-1]) D[i][j] = D[i-1][j-1]; // In accordance with the algorithm
				else {
					D[i][j] = 1 + min(D[i-1][j-1], min(D[i-1][j], D[i][j-1]));
				}
				if(debug) cout << D[i][j] << " | ";
			}
			if(debug) cout << "\n-----------------------------"<< endl;
		}
		if(debug) cout << "word = " << word<< " Size = " << wordLen << " Dist = " << D[toSearchLen][wordLen] << endl;
		distance = D[toSearchLen][wordLen];

		// act accordingly
		if(distance == 0) return true;  // exact match found, terminate search
		if(distance > k) continue;		// difference is too big
		else							// acceptable difference, add word to options
			if(distance > 0 && distance <= k) {
			optionWord.opt = (*it);
			optionWord.dist = distance;
			//options.push_back((*it));
			options.push_back(optionWord);
			if(debug) cout << "Option found:  = " << (*it) << endl;
		}
	}

	return false;
}


void SpellChecker::processDictionary(string dicName, vector<string> & dicWords) {
	string str;
	ifstream dicfile;

	int a;
	char word[30];

	dicfile.open(dicName.c_str(), ifstream::in);
	if(!dicfile.good()) printf("Error opening ");

	while (getline(dicfile, str)) {
		if(debug) cout << "Read: " << str << endl;
		a=0;
		for(unsigned int i=0; i<str.size();i++) {
			if(isalpha(str[i])) {
				word[a]=(tolower(str[i]));
				a++;
			}
			else break;
		}
		word[a]='\0';
		if(strlen(word)==0) continue;
		if(debug) cout << "Substituted: " << word << endl;
		dicWords.push_back(word);
	}
}

// Compare distance in options. For sorting purpose.
bool compareDist(option i, option j){ return (i.dist<j.dist); }


void SpellChecker::ReadFile(string fname, string dict) {

	string str, word;
	ifstream infile;
	fstream outfile;
	char Res[30];
	int i=0, j=0;
	//vector<string> options;
	vector<string> dicWords;
	string dictFile;
	vector<option> options;

	if(dict == "eng") dictFile = "en-US.dic";
	else if(dict == "pt") dictFile = "pt-PT.dic";
	else {
		cout << "No dictionary found" << endl;
		return;
	}

	// Load all dictionary words in vector dicWords
	dicWords.clear();
	processDictionary(dictFile, dicWords);

	// Open file for spell check
	infile.open(fname.c_str(), ifstream::in);
	if(!infile.good()) {
		cout << "Error opening "<< fname << endl;
		return;
	}
	cout << fname << endl;

	// Open the output file
	outfile.open("output.txt");
	if(!outfile.good()) printf("Error opening output.txt \n");
	outfile.clear();

	this->wordsCounter = 0;

	// Read file word by word
	while (infile >> word) {
		this->wordsCounter++;
		//Remove punctuation and make all lower case
		i=0; j=0;
		while(word[i]) {

			if(isalpha(word[i])) {
				Res[j]=(tolower(word[i]));
				j++;
			}
			i++;
		}
		Res[j]='\0';
		if(debug) cout << word << " -> " << Res << endl;

		// Run approximate match algorithm. If exact match found - do nothing
		options.clear();

		if(!this->approximateStringMatching(dicWords, Res, options)) {
			outfile << " *" << word ;
			if(options.size() > 0 ){
				outfile << "* [" ;
				sort(options.begin(), options.end(), compareDist);
				for(unsigned int i = 0; i < options.size() && i < 10; i++) {
					cout << "Mistake in word: " << Res << ". Suggested option: " << options[i].opt << endl;
					outfile << options[i].opt <<"; " ;
				}
				cout << endl;
				outfile << "] " ;
			}
			else {
				cout << "Mistake in word: " << Res << ". No options found.\n"<< endl;
				outfile << "* [NO OPTIONS] " ;
			}
		}
		else outfile << word << " ";

	}
	cout << "Total words: " << wordsCounter << endl;
	infile.close();
	outfile.close();
}


void SpellChecker::ui() {
	bool work = true;
	string resposta;
	ifstream testingFile;

	clock_t startTime, endTime;

	int option;

	do {
		cout << "Active dictionary: " << this->dictionary << endl;
		cout << "Document to correct: " << this->fileToCheck << endl;

		cout << "Choose your option:" << endl;
		cout << "1 - Change dictionary." << endl;
		cout << "2 - Change document to correct." << endl;
		cout << "3 - Check document spelling." << endl;
		cout << "4 - Exit." << endl;
		getline(cin, resposta);
		option = atoi(resposta.c_str());
		switch(option) {
		case 1:
			cout << "Please choose the dictionary (eng or pt):" << endl;
			getline(cin, resposta);
			if(resposta == "eng" || resposta == "pt")
				this->dictionary = resposta;
			else cout << "This dictionary is not supported!" << endl;
			break;
		case 2:
			cout << "Please indicate the name for the document:" << endl;
			getline(cin, resposta);
			testingFile.open(resposta.c_str(), ifstream::in);
			//cout << "You entered:" << resposta.c_str() << endl;
			if(testingFile.good()) {
				this->fileToCheck = resposta;
			}
			else {
				cout << "Error opening text file!" << endl;
			}
			testingFile.close();
			break;
		case 3:
			startTime = clock();
			ReadFile(this->fileToCheck, this->dictionary);
			endTime = clock();
			cout <<"Time of execution: "
				 << double(endTime - startTime) / CLOCKS_PER_SEC
				 << " seconds.\n" << endl;
			break;
		case 4:
			return;
			break;
		default:
			cout << "Please make a valid choice." << endl;
			break;
		}
	} while (work);
}

